package rtpencode

import (
	"fmt"
	"time"

	"github.com/pion/rtp"
	"github.com/yapingcat/gomedia/mpeg2"
	"gogs.smarteye.com/smarteye/gortc/pkg/format"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpac3"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpav1"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtph264"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtph265"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtplpcm"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpmjpeg"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpmpeg1audio"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpmpeg1video"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpmpeg4audio"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpmpeg4video"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpps"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpsimpleaudio"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpvp8"
	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpvp9"

	"gogs.smarteye.com/smarteye/gortc/pkg/format/rtpfile"
)

type RtpEncoderDecoder struct {
	f format.Format

	h264Encoder *rtph264.Encoder
	h264Decoder *rtph264.Decoder

	h265Encoder *rtph265.Encoder
	h265Decoder *rtph265.Decoder

	g711Encoder *rtplpcm.Encoder
	g711Decoder *rtplpcm.Decoder

	lpcmEncoder *rtplpcm.Encoder
	lpcmDecoder *rtplpcm.Decoder

	ac3Encoder *rtpac3.Encoder
	ac3Decoder *rtpac3.Decoder

	g722Encoder *rtpsimpleaudio.Encoder
	g722Decoder *rtpsimpleaudio.Decoder

	g726Encoder *rtpsimpleaudio.Encoder
	g726Decoder *rtpsimpleaudio.Decoder

	opusEncoder *rtpsimpleaudio.Encoder
	opusDecoder *rtpsimpleaudio.Decoder

	av1Encoder *rtpav1.Encoder
	av1Decoder *rtpav1.Decoder

	vp8Encoder *rtpvp8.Encoder
	vp8Decoder *rtpvp8.Decoder

	vp9Encoder *rtpvp9.Encoder
	vp9Decoder *rtpvp9.Decoder

	psEncoder *rtpps.Encoder
	psDecoder *rtpps.Decoder

	mpeg4AudioEncoder *rtpmpeg4audio.Encoder
	mpeg4AudioDecoder *rtpmpeg4audio.Decoder

	mpeg4VideoEncoder *rtpmpeg4video.Encoder
	mpeg4VideoDecoder *rtpmpeg4video.Decoder

	mpeg1AudioEncoder *rtpmpeg1audio.Encoder
	mpeg1AudioDecoder *rtpmpeg1audio.Decoder

	mpeg1VideoEncoder *rtpmpeg1video.Encoder
	mpeg1VideoDecoder *rtpmpeg1video.Decoder

	mjpegEncoder *rtpmjpeg.Encoder
	mjpegDecoder *rtpmjpeg.Decoder

	fileEncoder *rtpfile.Encoder
	fileDecoder *rtpfile.Decoder
}

func NewRtpEncoderDecoder(f format.Format) *RtpEncoderDecoder {
	return &RtpEncoderDecoder{
		f: f,
	}
}

func (tis *RtpEncoderDecoder) CreateEncoder() error {
	f := tis.f

	switch f := f.(type) {
	case *format.H264:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.h264Encoder = encoder
		}
	case *format.H265:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.h265Encoder = encoder
		}
	case *format.G711:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.g711Encoder = encoder
		}
	case *format.Generic:

	case *format.G722:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.g722Encoder = encoder
		}
	case *format.G726:
		encoder := &rtpsimpleaudio.Encoder{
			PayloadType: f.PayloadTyp,
		}

		if err := encoder.Init(); err != nil {
			return err
		}

		tis.g726Encoder = encoder

	case *format.AC3:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.ac3Encoder = encoder
		}
	case *format.LPCM:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.lpcmEncoder = encoder
		}
	case *format.Opus:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.opusEncoder = encoder
		}
	case *format.VP8:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.vp8Encoder = encoder
		}
	case *format.VP9:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.vp9Encoder = encoder
		}
	case *format.AV1:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.av1Encoder = encoder
		}
	case *format.PS:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.psEncoder = encoder
		}
	case *format.MPEG4Audio:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.mpeg4AudioEncoder = encoder
		}
	case *format.MPEG4Video:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.mpeg4VideoEncoder = encoder
		}
	case *format.MPEGTS:

	case *format.Speex:
	case *format.MJPEG:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.mjpegEncoder = encoder
		}
	case *format.MPEG1Audio:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.mpeg1AudioEncoder = encoder
		}
	case *format.MPEG1Video:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.mpeg1VideoEncoder = encoder
		}
	case *format.File:
		if encoder, err := f.CreateEncoder(); err != nil {
			return err
		} else {
			tis.fileEncoder = encoder
		}
	default:
		return fmt.Errorf("unknown format. %v", f.Codec())
	}

	return nil
}

func (tis *RtpEncoderDecoder) CreateDecoder() error {
	f := tis.f

	switch f := f.(type) {
	case *format.H264:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.h264Decoder = decoder
		}
	case *format.H265:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.h265Decoder = decoder
		}
	case *format.G711:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.g711Decoder = decoder
		}
	case *format.Generic:
	case *format.G722:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.g722Decoder = decoder
		}
	case *format.G726:
		decoder := &rtpsimpleaudio.Decoder{}
		if err := decoder.Init(); err != nil {
			return err
		}

		tis.g726Decoder = decoder
	case *format.AC3:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.ac3Decoder = decoder
		}
	case *format.LPCM:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.lpcmDecoder = decoder
		}
	case *format.Opus:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.opusDecoder = decoder
		}
	case *format.VP8:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.vp8Decoder = decoder
		}
	case *format.VP9:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.vp9Decoder = decoder
		}
	case *format.AV1:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.av1Decoder = decoder
		}
	case *format.PS:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.psDecoder = decoder
		}
	case *format.MPEG4Audio:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.mpeg4AudioDecoder = decoder
		}
	case *format.MPEG4Video:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.mpeg4VideoDecoder = decoder
		}
	case *format.MPEGTS:

	case *format.Speex:
	case *format.MJPEG:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.mjpegDecoder = decoder
		}
	case *format.MPEG1Audio:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.mpeg1AudioDecoder = decoder
		}
	case *format.MPEG1Video:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.mpeg1VideoDecoder = decoder
		}
	case *format.File:
		if decoder, err := f.CreateDecoder(); err != nil {
			return err
		} else {
			tis.fileDecoder = decoder
		}
	default:
		return fmt.Errorf("unknown format. %v", f.Codec())
	}

	return nil
}

func (tis *RtpEncoderDecoder) Encode(data [][]byte) ([]*rtp.Packet, error) {

	f := tis.f

	switch f := f.(type) {
	case *format.H264:
		if value := tis.h264Encoder; value != nil {
			return value.Encode(data)
		}
	case *format.H265:
		if value := tis.h265Encoder; value != nil {
			return value.Encode(data)
		}
	case *format.G711:
		if value := tis.g711Encoder; value != nil {
			var result []*rtp.Packet
			for _, item := range data {
				packets, err := value.Encode(item)
				if err != nil {
					return nil, err
				} else {
					result = append(result, packets...)
				}
			}
			return result, nil
		}
	case *format.Generic:
	case *format.G722:
	case *format.G726:
		if value := tis.g726Encoder; value != nil {
			var result []*rtp.Packet
			for _, item := range data {
				packet, err := value.Encode(item)
				if err != nil {
					return nil, err
				} else {
					result = append(result, packet)
				}
			}
			return result, nil
		}
	case *format.AC3:
	case *format.LPCM:
	case *format.Opus:
		if value := tis.opusEncoder; value != nil {
			var result []*rtp.Packet
			for _, item := range data {
				packet, err := value.Encode(item)
				if err != nil {
					return nil, err
				} else {
					result = append(result, packet)
				}
			}
			return result, nil
		}
	case *format.VP8:
		if value := tis.vp8Encoder; value != nil {
			var result []*rtp.Packet
			for _, item := range data {
				packets, err := value.Encode(item)
				if err != nil {
					return nil, err
				} else {
					result = append(result, packets...)
				}
			}
			return result, nil
		}
	case *format.VP9:
		if value := tis.vp9Encoder; value != nil {
			var result []*rtp.Packet
			for _, item := range data {
				packets, err := value.Encode(item)
				if err != nil {
					return nil, err
				} else {
					result = append(result, packets...)
				}
			}
			return result, nil
		}
	case *format.AV1:
		if value := tis.av1Encoder; value != nil {
			return value.Encode(data)
		}
	default:
		return nil, fmt.Errorf("unknown format. %v", f.Codec())
	}

	return nil, fmt.Errorf("not find encoder. format %v", f.Codec())
}

func (tis *RtpEncoderDecoder) Decode(pkt *rtp.Packet) ([][]byte, error) {

	f := tis.f

	switch f := f.(type) {
	case *format.H264:
		if value := tis.h264Decoder; value != nil {
			return value.Decode(pkt)
		}
	case *format.H265:
		if value := tis.h265Decoder; value != nil {
			return value.Decode(pkt)
		}
	case *format.G711:
		if value := tis.g711Decoder; value != nil {
			if data, err := value.Decode(pkt); err != nil {
				return nil, err
			} else {
				return [][]byte{data}, nil
			}
		}
	case *format.Generic:
	case *format.G722:
	case *format.G726:
		if value := tis.g726Decoder; value != nil {
			if data, err := value.Decode(pkt); err != nil {
				return nil, err
			} else {
				return [][]byte{data}, nil
			}
		}
	case *format.AC3:
	case *format.LPCM:
	case *format.Opus:
		if value := tis.opusDecoder; value != nil {
			if data, err := value.Decode(pkt); err != nil {
				return nil, err
			} else {
				return [][]byte{data}, nil
			}
		}
	case *format.VP8:
		if value := tis.vp8Decoder; value != nil {
			if data, err := value.Decode(pkt); err != nil {
				return nil, err
			} else {
				return [][]byte{data}, nil
			}
		}
	case *format.VP9:
		if value := tis.vp9Decoder; value != nil {
			if data, err := value.Decode(pkt); err != nil {
				return nil, err
			} else {
				return [][]byte{data}, nil
			}
		}
	case *format.AV1:
		if value := tis.av1Decoder; value != nil {
			return value.Decode(pkt)
		}
	case *format.File:
		if value := tis.fileDecoder; value != nil {
			if data, err := value.Decode(pkt); err != nil {
				return nil, err
			} else {
				return [][]byte{data}, nil
			}
		}
	default:
		return nil, fmt.Errorf("unknown format. %v", f.Codec())
	}

	return nil, fmt.Errorf("not find encoder. format %v", f.Codec())
}

func (tis *RtpEncoderDecoder) EncodePS(frame []byte, cid mpeg2.PS_STREAM_TYPE, pts time.Duration) ([]*rtp.Packet, error) {
	f := tis.f

	switch f.(type) {
	case *format.PS:
		if value := tis.psEncoder; value != nil {
			return value.Encode(frame, cid, pts)
		}
	default:
		return nil, fmt.Errorf("unknown format. %v", f.Codec())
	}

	return nil, fmt.Errorf("not find encoder. format %v", f.Codec())
}

func (tis *RtpEncoderDecoder) DecodePS(pkt *rtp.Packet, onFrame func(data []byte, cid mpeg2.PS_STREAM_TYPE, pts time.Duration)) error {

	f := tis.f

	switch f := f.(type) {
	case *format.PS:
		if value := tis.psDecoder; value != nil {
			return value.Decode(pkt, onFrame)
		}

	default:
		return fmt.Errorf("unknown format. %v", f.Codec())
	}

	return fmt.Errorf("not find encoder. format %v", f.Codec())
}
